package com.jar.springbootsecuritydemo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jar.springbootsecuritydemo.config.SecurityUserFactory;
import com.jar.springbootsecuritydemo.mapper.SysMenuMapper;
import com.jar.springbootsecuritydemo.mapper.SysRoleMapper;
import com.jar.springbootsecuritydemo.mapper.SysRoleMenuMapper;
import com.jar.springbootsecuritydemo.mapper.SysUserMapper;
import com.jar.springbootsecuritydemo.mapper.SysUserRoleMapper;
import com.jar.springbootsecuritydemo.pojo.bo.Audience;
import com.jar.springbootsecuritydemo.pojo.bo.SecurityUser;
import com.jar.springbootsecuritydemo.pojo.entity.SysMenu;
import com.jar.springbootsecuritydemo.pojo.entity.SysRole;
import com.jar.springbootsecuritydemo.pojo.entity.SysRoleMenu;
import com.jar.springbootsecuritydemo.pojo.entity.SysUser;
import com.jar.springbootsecuritydemo.pojo.entity.SysUserRole;
import com.jar.springbootsecuritydemo.utils.JwtTokenUtil;
import com.jar.springbootsecuritydemo.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@RestController
@Slf4j
public class LoginController {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private Audience audience;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @RequestMapping("/login")
    public Object login(String username, String password, Boolean isRememberMe) {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.eq(SysUser.COL_USER_NAME, username);

        // 根据用户名找此用户
        SysUser user = sysUserMapper.selectOne(wrapper);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        //校验密码
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        boolean matches = encoder.matches(password, user.getPassword());
        if (!matches) {
            throw new BadCredentialsException("密码不正确");
        }
        if ("1".equals(user.getStatus())) {
            // 还可以加一些其他信息的判断，比如用户账号已停用等判断
            throw new LockedException("该用户已被停用");
        }

        // 查看这个用户拥有哪些角色
        List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectList(
                new QueryWrapper<SysUserRole>().eq(SysUser.COL_USER_ID, user.getUserId())
        );

        List<String> roleIds = sysUserRoles.stream().map(res -> res.getRoleId() + "").collect(Collectors.toList());

        List<SysRole> sysRoles = sysRoleMapper.selectList(
                new QueryWrapper<SysRole>().in(SysRole.COL_ROLE_ID, roleIds)
        );

        List<String> roleNameList = sysRoles.stream().map(SysRole::getRoleName).collect(Collectors.toList());


        List<SysRoleMenu> sysRoleMenus = sysRoleMenuMapper.selectList(
                new QueryWrapper<SysRoleMenu>().in(SysRoleMenu.COL_ROLE_ID, roleIds)
        );

        List<Integer> menuIds = sysRoleMenus.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());

        List<SysMenu> sysMenus = sysMenuMapper.selectList(
                new QueryWrapper<SysMenu>().in(SysMenu.COL_MENU_ID, menuIds)
        );
        Set<String> permList = sysMenus.stream().map(SysMenu::getPerms).collect(Collectors.toSet());

        String jwtToken = jwtTokenUtil.createJWT(user.getUserName()
                , user.getUserId()
                , roleNameList
                , audience.getIssuer()
                , audience.getSecret()
        );

        user.setSysRoles(roleNameList);

        //得到tokenKey  【login_tokens:userName】
        String tokenKey = jwtTokenUtil.getTokenKey(user.getUserName());

        //把token放入到redis中
        redisUtil.setEx(tokenKey, jwtToken, 300, TimeUnit.SECONDS);


        SecurityUser securityUser = SecurityUserFactory.create(user, permList);
        try {
            String securityUserString = objectMapper.writeValueAsString(securityUser);

            String loginUserKey = jwtTokenUtil.getLoginUserKey(user.getUserName());

            redisUtil.setEx(loginUserKey, securityUserString, 300, TimeUnit.SECONDS);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            log.error("用户对象转换失败：【{}】",securityUser);
        }

        return jwtTokenUtil.getTokenWithHead(jwtToken);
    }

    @RequestMapping("/logout")
    public Object logout() {
        SecurityContextHolder.clearContext();
        Map<String, Object> map = new HashMap<>();
        map.put("code", 401);
        map.put("msg", "未登录");
        return map;
    }

}
